﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.Schema;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using haptic.src.engine.design;
using haptic.src.engine.render;

namespace haptic.src.engine.animation
{
    [Serializable]
    [XmlRootAttribute("state")]
    public class State : IInstanciable<StateInstance>, IXmlSerializable
    {
        #region Constructors
        /// <summary>
        /// Only for XML serialization
        /// </summary>
        public State(){}
        public State(string sName)
        {
            m_sName = sName;
        }
        public StateInstance CreateInstance()
        {
            return new StateInstance(this);
        }
        #endregion

        #region Load
        public void Load(ContentManager oContent)
        {
            foreach (StateEffect oEffect in m_oEffects)
            {
                oEffect.Load(oContent);
            }
        }
        #endregion

        #region Serialization
        public XmlSchema GetSchema()
        {
            return null;
        }
        public void WriteXml(XmlWriter w)
        {
            // name
            w.WriteAttributeString("name", m_sName);

            // duration
            w.WriteAttributeString("duration", m_fDuration.ToString());

            XmlSerializerNamespaces oNs = new XmlSerializerNamespaces();
            oNs.Add("", "");

            // Controller
            if (m_oController != null)
            {
                w.WriteStartElement("controller");
                w.WriteAttributeString("type", m_oController.GetType().ToString());
                XmlSerializer oSrlz = new XmlSerializer(m_oController.GetType());
                oSrlz.Serialize(w, m_oController, oNs);
                w.WriteEndElement();
            }

            // effects
            w.WriteStartElement("effects");
            foreach (StateEffect oEffect in m_oEffects)
            {
                w.WriteStartElement("effect");
                w.WriteAttributeString("type", oEffect.GetType().ToString());
                XmlSerializer oSrlz = new XmlSerializer(oEffect.GetType());
                oSrlz.Serialize(w, oEffect, oNs);
                w.WriteEndElement();
            }
            w.WriteEndElement();
        }
        public void ReadXml(XmlReader r)
        {
            // name
            m_sName = r.GetAttribute("name");

            // duration
            m_fDuration = float.Parse(r.GetAttribute("duration"));

            // controller
            m_oController = null;
            if (r.ReadToFollowing("controller"))
            {
                Type oType = Type.GetType(r.GetAttribute("type"));
                XmlSerializer oSrlz = new XmlSerializer(oType);
                r.ReadToFollowing(oType.Name);
                m_oController = (IStateController)oSrlz.Deserialize(r);
                m_oController.Initialize();
            }

            // effects
            m_oEffects.Clear();
            if (r.ReadToFollowing("effects"))
            {
                XmlReader r2 = r.ReadSubtree();
                while (r2.ReadToFollowing("effect"))
                {
                    Type oType = Type.GetType(r2.GetAttribute("type"));
                    XmlSerializer oSrlz = new XmlSerializer(oType);
                    r2.ReadToDescendant(oType.Name);
                    StateEffect oEffect = (StateEffect)oSrlz.Deserialize(r2);
                    oEffect.Initialize();
                    m_oEffects.Add(oEffect);
                }
            }
        }
        #endregion

        #region Attributes
        [XmlAttribute("name")]
        public string m_sName;
        [XmlIgnore]
        public IStateController m_oController;
        [XmlAttribute("duration")]
        public float m_fDuration = 0.0f;
        private List<Transition> m_oTransitions = new List<Transition>();
        public List<StateEffect> m_oEffects = new List<StateEffect>();
        #endregion
    }

    public class StateInstance
    {
        public delegate void CbOnEnter();
        public delegate void CbOnUpdate(GameTime oTime);
        public delegate void CbOnExit();

        #region Contructors
        public StateInstance(State oState)
        {
            m_oState = oState;
            if (m_oState.m_oController != null)
                m_oControllerInstance = m_oState.m_oController.CreateInstance();
        }
        #endregion

        #region Update
        public void Enter()
        {
            if (m_cbOnEnter != null)
                m_cbOnEnter();
        }
        public void Exit()
        {
            if (m_cbOnExit != null)
                m_cbOnExit();
        }
        public void Update(GameTime oTime, AutomateInstance.Context oContext)
        {
            m_fCurrentTimeStep += (float)oTime.ElapsedGameTime.TotalSeconds;
            if (m_oControllerInstance != null)
                m_oControllerInstance.Update(this, oTime, oContext);

            // Update effects
            float fTimeRatio;
            if (m_oState.m_fDuration == 0.0f)
                fTimeRatio = m_fCurrentTimeStep;
            else
                fTimeRatio = m_fCurrentTimeStep / m_oState.m_fDuration;
            foreach (StateEffect oEffect in m_oState.m_oEffects)
                oEffect.Update(fTimeRatio, oContext);

            if (m_cbOnUpdate != null)
                m_cbOnUpdate(oTime);
        }
        #endregion

        #region Draw
        public void Draw(Renderer oRenderer, AutomateInstance.Context oDrawContext)
        {
            foreach (StateEffect oEffect in m_oState.m_oEffects)
            {
                oEffect.Draw(oRenderer, oDrawContext);
            }
        }
        #endregion

        #region Attributes
        public float m_fCurrentTimeStep = 0.0f;
        public CbOnEnter m_cbOnEnter;
        public CbOnUpdate m_cbOnUpdate;
        public CbOnExit m_cbOnExit;
        private State m_oState;
        public State oState { get { return m_oState; } }
        //private Automate m_oAutomate;
        private IStateControllerInstance m_oControllerInstance;
        #endregion
    }
}
